from abc import ABC, abstractmethod
from base.lib.Closeable import Closeable
from base.lib.Sync import Sync

class NodeTable(ABC, Closeable, Sync):
    '''
     * Store the node in the node table (if not already present) and return the allocated Id.
    '''
    # param Node
    # result NodeId
    @abstractmethod
    def getAllocateNodeId(self, node):
        pass

    '''
     * Look up node and return the NodeId - return NodeId.NodeDoesNotExist if not found
    '''
    # param Node
    # result NodeId
    @abstractmethod
    def getNodeIdForNode(self, node):
        pass

    '''
     * Look up node id and return the Node - return null if not found
    '''
    # param NodeId
    # result Node
    @abstractmethod
    def getNodeForNodeId(self, id):
        pass

    '''
     * Test whether the node table contains an entry for node
    '''
    # param Node
    # result boolean
    @abstractmethod
    def containsNode(self, node):
        pass

    '''
     * Test whether the node table contains an entry for node
    '''
    # param NodeId
    # result boolean
    @abstractmethod
    def containsNodeId(self, nodeId):
        pass

    '''
     * Bulk mapping from {@code Node} to {@code NodeId}, with allocation
     * if the {@code withAllocation} is true.
     * The returned list aligns with the input list.
    '''
    # param List<Node> boolean
    # result List<NodeId>
    @abstractmethod
    def bulkNodeToNodeId(self, nodes, withAllocation):
        pass

    '''
     * Bulk mapping from {@code NodeId} to {@code Node}
    '''
    # param List<NodeId>
    # result List<Node> 
    @abstractmethod
    def bulkNodeIdToNode(self, nodeIds):
        pass

    '''
     * Bulk lookup
     * List<NodeId> getAllocateNodeIdBulk(List<Node> nodes):
        pass
     * <p>
     *  Iterate over all nodes (not necessarily fast).  Does not include inlined NodeIds
    '''
    # param 
    # result Iterator<Pair<NodeId, Node>> 
    @abstractmethod
    def all(self):
        pass

    '''
     * Anything there?
    '''
    # param 
    # result boolean
    @abstractmethod
    def isEmpty(self):
        pass

    '''
     * Return a NodeTable if this instance wraps another, else return null
    '''
    # param 
    # result NodeTable
    @abstractmethod
    def wrapped(self):
        pass

    '''
     * Return the base NodeTable, the end of the wrapped chain
    '''
    # param
    # result NodeTable
    def baseNodeTable(self):
        nt = self
        nt2 = None
        while nt.wrapped() is not None:
            nt = nt.wrapped()
        return nt